﻿using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using DVK.SP2007;
using DVK.SP2007.UniversalClient.DataAccess;
using Microsoft.SharePoint.Administration;
using Microsoft.SharePoint;
using DVK.SP2007.Constants;

namespace DVK.SP2007
{
    /// <summary>
    /// The Organisations list synchronization job.
    /// Adds or Updates organisations information in the sharepoint list based on the information in the UK database.
    /// </summary>
    public class SynchronizeOrganisationsJob : SPJobDefinition
    {
        private Logger _logger;
        /// <summary>
        /// Initializes a new instance of the <see cref="SynchronizeOrganisationsJob"/> class.
        /// </summary>
        public SynchronizeOrganisationsJob() : base()
        {
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SynchronizeOrganisationsJob"/> class.
        /// </summary>
        /// <param name="jobName">Name of the job.</param>
        /// <param name="webApplication">The web application.</param>
        public SynchronizeOrganisationsJob(string jobName, SPWebApplication webApplication)
            : base(jobName, webApplication, null, SPJobLockType.Job)
        {
            
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="SynchronizeOrganisationsJob"/> class.
        /// </summary>
        /// <param name="jobName">Name of the job.</param>
        /// <param name="service">The service.</param>
        /// <param name="server">The server.</param>
        /// <param name="lockType">Type of the lock.</param>
        public SynchronizeOrganisationsJob(string jobName, SPService service, SPServer server, SPJobLockType lockType)
            : base(jobName, service, server, lockType)
        {
        }

        /// <summary>
        /// Executes the job definition.
        /// </summary>
        /// <param name="targetInstanceId">For target types of <see cref="T:Microsoft.SharePoint.Administration.SPContentDatabase"></see> this is the database ID of the content database being processed by the running job. This value is Guid.Empty for all other target types.</param>
        public override void Execute(Guid targetInstanceId)
        {
            this._logger = JobHelper.GetLoggerForSPJob(LoggingSources.SynchronizeOrganisationsJob, this.Properties);
            this._logger.LogInformative("{0}: Logger is logging on {1} level.", LoggingSources.SynchronizeOrganisationsJob, _logger.Switch.Level.ToString());

            //Watcher to time the jobs execution.
            Stopwatch watcher = new Stopwatch();
            watcher.Start();
            
            base.Execute(targetInstanceId);

            try
            {
                var siteUrl = this.Properties[Settings.DvkSiteUrl] as string;
                
                using (SPSite site = new SPSite(siteUrl))
                using (SPWeb web = site.RootWeb)
                {
                    DvkSettingsRepository settingsManager = new DvkSettingsRepository(web);
                    string connectionString = 
                        settingsManager.QuerySetting(Settings.DvkUKDatabaseConnectionString);

                    if (connectionString == null)
                    {
                        _logger.LogError(
                            LoggerEvents.ConnectionStringIsNotConfigured,
                            "{0}: ConnectionString is null.",
                            web.Url
                            );
                        return;
                    }

                    SynchronizeOrganisations(web, connectionString);
                }
            }
            catch (Exception exc)
            {
                _logger.LogError(
                    LoggerEvents.UnhandledExceptionEncountered,
                    exc,
                    "DVK Organizations synchronization was interrupted."
                    );
            }
            finally
            {
                watcher.Stop();
                _logger.LogInformative(
                        "Job last runtime: {0} ms.",
                        watcher.ElapsedMilliseconds
                    );
            }
        }

        /// <summary>
        /// Synchronizes the organisations in the sharepoint list.
        /// </summary>
        /// <param name="web">The web.</param>
        /// <param name="connectionString">The connection string.</param>
        private void SynchronizeOrganisations(SPWeb web, string connectionString)
        {
            SPList organisationsList = web.GetList(Urls.ListOrganisations);

            List<dhl_organization> organisations = null;
            using (UniversalClientDao universalClient = new UniversalClientDao(connectionString))
            {
                organisations = universalClient.GetAllOrganisations();
            }

            _logger.LogInformative(
                    "{0}: Found {1} organisations from dvk. ",
                    web.Url,
                    organisations.Count
                );

            foreach (var organisation in organisations)
            {
                try
                {
                    //Check if the organisation already exists in the list
                    SPQuery organisationsQuery = new SPQuery();
                    organisationsQuery.RowLimit = 1;
                    organisationsQuery.Query = "<Where><And>" +
                        "<Eq><FieldRef Name='" + Fields.OrganisationName.Name + "' /><Value Type='Text'>" + organisation.org_name + "</Value></Eq>" +
                        "<Eq><FieldRef Name='" + Fields.OrganisationCode.Name + "' /><Value Type='Text'>" + organisation.org_code + "</Value></Eq>" +
                        "</And></Where>";

                    SPListItemCollection listItemCollection = organisationsList.GetItems(organisationsQuery);
                    if (listItemCollection.Count > 0)
                    {
                        //Update if it does
                        AddOrUpdateOrganisation(listItemCollection[0], organisation);
                    }
                    else
                    {
                        //Add if it doesnt

                        SPListItem item = organisationsList.Items.Add();
                        AddOrUpdateOrganisation(item, organisation);
                        _logger.LogInformative(
                                "{0}: Added new organisation '{1}' to SP organisations list.",
                                web.Url,
                                organisation.org_name
                            );
                    }
                }
                catch (Exception exc)
                {
                    _logger.LogError(
                        LoggerEvents.CouldNotAddOrganisationToSPList,
                        exc,
                        "{0}: Adding organisation to Sharepoint list failed (Name: {1}, Code: {2}).",
                        web.Url,
                        organisation.org_name,
                        organisation.org_code
                        );
                }
            }
        }

        /// <summary>
        /// Adds or updates the organisation (listitem).
        /// </summary>
        /// <param name="listItem">The list item.</param>
        /// <param name="organisation">The organisation.</param>
        public void AddOrUpdateOrganisation(SPListItem listItem, dhl_organization organisation)
        {
            listItem[Fields.OrganisationName.Name] = organisation.org_name;
            listItem[Fields.OrganisationCode.Name] = organisation.org_code;
            listItem[Fields.DvkCapable.Name] = organisation.dhl_capable;
            listItem[Fields.DvkDirectCapable.Name] = organisation.dhl_direct_capable;
            listItem[Fields.DvkDirectCapableProducerName.Name] = organisation.dhl_direct_producer_name;
            listItem.Update();
        }
    }
}